#ifndef CACHE_WRITE_BACK_HPP
#define CACHE_WRITE_BACK_HPP


#include "Cache.hpp"
#include "CacheLine.hpp"
#include <string>
#include <vector>

template <typename TData>
class CacheWriteBack : public Cache<TData> {
public:
/*CacheWriteBack (uint8_t setBits, uint8_t assocBits, uint8_t blockBits,
        bool directory)
  : Cache<TData>(setBits, assocBits, blockBits,directory)
{
*/
CacheWriteBack (uint32_t cacheSize, uint32_t associativity, uint32_t lineSize,
        bool directory,uint32_t pagesInDirectory)
  : Cache<TData>(cacheSize,associativity,lineSize,directory,pagesInDirectory)
{
}
~CacheWriteBack(){}

bool
find (address_t address)
{
   CacheSet<TData> * s;
   CacheAccess access;

   s = this->cacheSet(address);
   access = s->find(this->blockAddress(address));

   return CacheAccessIsHit(access);
}

bool
isDirty (address_t address)
{
   CacheSet<TData> * s;
   CacheAccess access;

   s = this->cacheSet(address);
   access = s->find(this->blockAddress(address));

   return CacheAccessIsDirty(access);
}

bool
read (address_t address,address_t &evict,Directory &dirEvict,
        Directory & dirEntry, TData & data,uint32_t thread=0,uint32_t dirPage=0)
{
   CacheSet<TData> * s;
   CacheAccess access;

   s = this->cacheSet(address);
   access= s->read(this->blockAddress(address), evict,dirEvict,dirEntry,data,thread,dirPage);

   return CacheAccessIsHit(access);
}

CacheAccess
read2 (address_t address,address_t &evict,Directory & dirEvict,
        Directory & dirEntry, TData & data,uint32_t thread=0,uint32_t dirPage=0)
{
   CacheSet<TData> * s;

   s = this->cacheSet(address);
   return  s->read(this->blockAddress(address),evict,dirEvict,dirEntry,data,thread,dirPage);
}

CacheAccess
write (address_t address,uint32_t thread,TData data)
{
    CacheSet<TData> * s;

    s = this->cacheSet(address);
    return s->writeNonAllocate(this->blockAddress(address),thread,data);
}

CacheAccess
writeAllocate (const address_t address,address_t &evict,
        uint32_t thread,TData data)
{
    CacheSet<TData> * s;

    s = this->cacheSet(address);
    return s->writeAllocate(this->blockAddress(address),evict,thread,data);
}

bool
invalidate (address_t address)
{
   CacheSet<TData> * s;

   s = this->cacheSet(address);
   return s->invalidate(this->blockAddress(address));
}


bool
flush (address_t address)
{
    CacheSet<TData> * s;

    s = this->cacheSet(address);
    return s->flush(this->blockAddress(address));
}

void
flushAll ()
{

    for (ssize_t i = 0; i < this->_nSets; i++) {
         this->_sets[i]->flush();
    }
}
uint32_t
flushDirEntry (address_t address,uint32_t page,uint32_t thread)
{
    CacheSet<TData> * s;

    s = this->cacheSet(address);
    return s->flushDirEntry(this->blockAddress(address),page,thread);
}


std::vector<CacheLine<TData> *> * flush ()
{
  std::vector<CacheLine<TData> *> * lines=NULL;

  return lines;
}

Cache<TData> *
copy () const
{
  Cache<TData> * cache = new CacheWriteBack<TData>(this->_setBits, this->_assocBits, this->_blockBits,this->_directory,this->_pagesInDirectory);
  return cache;
}

void
flushDirectory(uint32_t * evictionResults)
{

    for (ssize_t i = 0; i < this->_nSets; i++) {
        this->_sets[i]->flush();
        /*
        typename std::vector<CacheLine<TData> *>::const_iterator iter;
        for (iter = lines->begin(); iter != lines->end(); ++iter ) {
            uint32_t count = (*iter)->countDirectory();
            evictionResults[count]++;
        }*/
    }
}

void
flushPermanentDirectory(uint32_t * evictionResults)
{
    for (ssize_t i = 0; i < this->_nSets; i++) {
        this->_sets[i]->flush();
        /*
           typename std::vector<CacheLine<TData> *>::const_iterator iter;
           for (iter = lines->begin(); iter != lines->end(); ++iter ) {
            uint32_t count = (*iter)->countPermanentDirectory();
            evictionResults[count]++;
        }
        */
    }
}

};

#endif
